package org.luxor.commons.selenium.config.properties;

import org.luxor.commons.core.utils.SystemUtils;
import org.luxor.commons.selenium.constant.Browser;
import org.luxor.commons.selenium.constant.Mode;
import org.openqa.selenium.PageLoadStrategy;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
 * Selenium 全局配置
 *
 * @author Mr.Yan  @date: 2022/03/21
 */
@ConfigurationProperties(prefix = SeleniumProperties.PREFIX)
public class SeleniumProperties {
    public static final String PREFIX = "spring.selenium";

    private final Boolean DEFAULT_ENABLED = Boolean.TRUE;

    /**
     * 驱动地址(官方驱动 http://chromedriver.storage.googleapis.com/index.html)
     */
    public static final String DEFAULT_DRIVER = "/selenium/chromedriver";

    public static final Browser DEFAULT_BROWSER = Browser.CHROME;

    public static final Mode DEFAULT_MODE = Mode.UI;

    public static final Boolean DEFAULT_IMAGES_ENABLED = Boolean.TRUE;

    public static final Boolean DEFAULT_CAMOUFLAGE_ENABLED = Boolean.FALSE;

    public static final long DEFAULT_IMPLICITLY_WAIT_TIME_OUT_SECONDS = 7;

    public static final long DEFAULT_SCRIPT_TIME_OUT_SECONDS = 7;

    public static final long DEFAULT_PAGE_LOAD_TIME_OUT_SECONDS = 15;

    public static final long DEFAULT_SESSION_CREATE_TIME_OUT_SECONDS = 30;

    public static final int DEFAULT_SESSION_MAX = 8;

    public static final int DEFAULT_RETRY_NUMBER_MAX = 0;

    public static final String DEFAULT_DOWNLOAD_DIRECTORY = "/tmp/downloads";
    public static final String DEFAULT_USER_DATA_DIR = "/tmp/user";


    public static final WindowSize DEFAULT_WINDOW_SIZE = new WindowSize(WindowSize.DEFAULT_WIDTH, WindowSize.DEFAULT_HEIGHT);


    /**
     * 是否启用
     */
    private boolean enabled;

    /**
     * 浏览器类型
     */
    private Browser browser;

    /**
     * 浏览器位置
     */
    private String binaryLocation;

    /**
     * 运行方式(支持本地UI启动、本地命令行启动、本地无特征界面模式、以及远程集群连接三种方式)
     */
    private Mode mode;

    /**
     * 驱动地址
     */
    private String driver;

    /**
     * 远程地址
     */
    private String remoteUrl;

    /**
     * 代理
     */
    private SeleniumProxy proxy;

    /**
     * 加载图片吗?（默认值:true-加载图片）
     */
    private Boolean imagesEnabled;

    /**
     * 启用伪装功能,绕开反爬特征检测?（默认值:false）
     */
    private Boolean camouflageEnabled;

    /**
     * 驱动创建超时时间(单位秒,默认值:30)
     */
    private long sessionCreateTimeoutSeconds;

    /**
     * 隐式等待时间(单位秒,默认值:7)
     */
    private long implicitlyWaitTimeOutSeconds;

    /**
     * 异步脚本执行超时时间(单位秒,默认值:7)
     */
    private long scriptTimeoutSeconds;

    /**
     * 网页加载超时时间(单位秒,默认值:15)
     */
    private long pageLoadTimeoutSeconds;

    /**
     * 页面加载策略
     */
    private PageLoadStrategy pageLoadStrategy;

    /**
     * webDriver最小并发数(默认值:1)
     */
    private int sessionMin;

    /**
     * webDriver最大并发数(默认值:8)
     */
    private int sessionMax;

    /**
     * 重试次数限制(默认值:0,不允许重试)
     */
    private int retryNumMax;

    /**
     * 下载目录
     */
    private String downloadDirectory;

    /**
     * 用户数据目录
     */
    private String userDataDir;

    /**
     * 窗口大小
     */
    private WindowSize windowSize;

    public SeleniumProperties() {
        this.enabled = DEFAULT_ENABLED.booleanValue();
        this.browser = DEFAULT_BROWSER;
        this.driver = DEFAULT_DRIVER;
        this.mode = DEFAULT_MODE;
        this.imagesEnabled = DEFAULT_IMAGES_ENABLED;
        this.camouflageEnabled = DEFAULT_CAMOUFLAGE_ENABLED;
        this.sessionCreateTimeoutSeconds = DEFAULT_SESSION_CREATE_TIME_OUT_SECONDS;
        this.implicitlyWaitTimeOutSeconds = DEFAULT_IMPLICITLY_WAIT_TIME_OUT_SECONDS;
        this.scriptTimeoutSeconds = DEFAULT_SCRIPT_TIME_OUT_SECONDS;
        this.pageLoadTimeoutSeconds = DEFAULT_PAGE_LOAD_TIME_OUT_SECONDS;
        this.pageLoadStrategy = PageLoadStrategy.NORMAL;
        this.sessionMax = DEFAULT_SESSION_MAX;
        this.retryNumMax = DEFAULT_RETRY_NUMBER_MAX;
        this.downloadDirectory = DEFAULT_DOWNLOAD_DIRECTORY;
        this.userDataDir = System.getProperty("user.home") + DEFAULT_DRIVER;
        this.windowSize = DEFAULT_WINDOW_SIZE;
        this.binaryLocation = defaultBinaryLocation();
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public Browser getBrowser() {
        return browser;
    }

    public void setBrowser(Browser browser) {
        this.browser = browser;
    }

    public String getBinaryLocation() {
        return binaryLocation;
    }

    public void setBinaryLocation(String binaryLocation) {
        this.binaryLocation = binaryLocation;
    }

    public Mode getMode() {
        return mode;
    }

    public void setMode(Mode mode) {
        this.mode = mode;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getRemoteUrl() {
        return remoteUrl;
    }

    public void setRemoteUrl(String remoteUrl) {
        this.remoteUrl = remoteUrl;
    }

    public SeleniumProxy getProxy() {
        return proxy;
    }

    public void setProxy(SeleniumProxy proxy) {
        this.proxy = proxy;
    }

    public Boolean getImagesEnabled() {
        return imagesEnabled;
    }

    public void setImagesEnabled(Boolean imagesEnabled) {
        this.imagesEnabled = imagesEnabled;
    }

    public Boolean getCamouflageEnabled() {
        return camouflageEnabled;
    }

    public void setCamouflageEnabled(Boolean camouflageEnabled) {
        this.camouflageEnabled = camouflageEnabled;
    }

    public long getSessionCreateTimeoutSeconds() {
        return sessionCreateTimeoutSeconds;
    }

    public void setSessionCreateTimeoutSeconds(long sessionCreateTimeoutSeconds) {
        this.sessionCreateTimeoutSeconds = sessionCreateTimeoutSeconds;
    }

    public long getImplicitlyWaitTimeOutSeconds() {
        return implicitlyWaitTimeOutSeconds;
    }

    public void setImplicitlyWaitTimeOutSeconds(long implicitlyWaitTimeOutSeconds) {
        this.implicitlyWaitTimeOutSeconds = implicitlyWaitTimeOutSeconds;
    }

    public long getScriptTimeoutSeconds() {
        return scriptTimeoutSeconds;
    }

    public void setScriptTimeoutSeconds(long scriptTimeoutSeconds) {
        this.scriptTimeoutSeconds = scriptTimeoutSeconds;
    }

    public long getPageLoadTimeoutSeconds() {
        return pageLoadTimeoutSeconds;
    }

    public void setPageLoadTimeoutSeconds(long pageLoadTimeoutSeconds) {
        this.pageLoadTimeoutSeconds = pageLoadTimeoutSeconds;
    }

    public PageLoadStrategy getPageLoadStrategy() {
        return pageLoadStrategy;
    }

    public void setPageLoadStrategy(PageLoadStrategy pageLoadStrategy) {
        this.pageLoadStrategy = pageLoadStrategy;
    }

    public int getSessionMin() {
        return sessionMin;
    }

    public void setSessionMin(int sessionMin) {
        this.sessionMin = sessionMin;
    }

    public int getSessionMax() {
        return sessionMax;
    }

    public void setSessionMax(int sessionMax) {
        this.sessionMax = sessionMax;
    }

    public int getRetryNumMax() {
        return retryNumMax;
    }

    public void setRetryNumMax(int retryNumMax) {
        this.retryNumMax = retryNumMax;
    }

    public String getDownloadDirectory() {
        return downloadDirectory;
    }

    public void setDownloadDirectory(String downloadDirectory) {
        this.downloadDirectory = downloadDirectory;
    }

    public String getUserDataDir() {
        return userDataDir;
    }

    public void setUserDataDir(String userDataDir) {
        this.userDataDir = userDataDir;
    }

    public WindowSize getWindowSize() {
        return windowSize;
    }

    public void setWindowSize(WindowSize windowSize) {
        this.windowSize = windowSize;
    }

    private static String defaultBinaryLocation() {
        Set<String> possibles = new HashSet<>();
        if (SystemUtils.isMacOs() || SystemUtils.isLinux()) {
            List<String> names = Arrays.asList("google-chrome", "chromium", "chromium-browser", "chrome", "google-chrome-stable");
            for (String path : SystemUtils.getPath()) {
                for (String name : names) {
                    possibles.add(path + File.separator + name);
                }
            }
            if (SystemUtils.isMacOs()) {
                possibles.add("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome");
                possibles.add("/Applications/Chromium.app/Contents/MacOS/Chromium");
            }
        } else {
            List<String> paths = Arrays.asList(SystemUtils.getEnv("PROGRAMFILES"),
                    SystemUtils.getEnv("PROGRAMFILES(X86)"), SystemUtils.getEnv("LOCALAPPDATA"));
            List<String> middles = Arrays.asList("Google" + File.separator + "Chrome" + File.separator + "Application",
                    "Google" + File.separator + "Chrome Beta" + File.separator + "Application",
                    "Google" + File.separator + "Chrome Canary" + File.separator + "Application");
            for (String path : paths) {
                for (String middle : middles) {
                    possibles.add(path + File.separator + middle + File.separator + "chrome.exe");
                }
            }
        }
        for (String possible : possibles) {
            File file = new File(possible);
            if (file.exists() && file.canExecute()) {
                return file.getAbsolutePath();
            }
        }
        return null;
    }
}
